Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
46.86% covered (danger)
46.86%
97 / 207
40.88% covered (danger)
40.88%
74 / 181
4.51% covered (danger)
4.51%
12 / 266
8.33% covered (danger)
8.33%
2 / 24
CRAP
0.00% covered (danger)
0.00%
0 / 1
DAO
46.86% covered (danger)
46.86%
97 / 207
40.88% covered (danger)
40.88%
74 / 181
4.51% covered (danger)
4.51%
12 / 266
8.33% covered (danger)
8.33%
2 / 24
10263.56
0.00% covered (danger)
0.00%
0 / 1
 __construct
75.00% covered (warning)
75.00%
3 / 4
75.00% covered (warning)
75.00%
3 / 4
33.33% covered (danger)
33.33%
1 / 3
0.00% covered (danger)
0.00%
0 / 1
5.67
 retrieve
83.33% covered (warning)
83.33%
5 / 6
75.00% covered (warning)
75.00%
3 / 4
33.33% covered (danger)
33.33%
1 / 3
0.00% covered (danger)
0.00%
0 / 1
5.67
 retrieveRange
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 1
56
 countRecords
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 concat
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 update
83.33% covered (warning)
83.33%
5 / 6
75.00% covered (warning)
75.00%
3 / 4
33.33% covered (danger)
33.33%
1 / 3
0.00% covered (danger)
0.00%
0 / 1
5.67
 replace
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getInsertId
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 _getInsertId
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 datetimeToDB
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 dateToDB
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 datetimeFromDB
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 dateFromDB
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 convertFromDB
84.21% covered (warning)
84.21%
16 / 19
77.27% covered (warning)
77.27%
17 / 22
16.67% covered (danger)
16.67%
4 / 24
0.00% covered (danger)
0.00%
0 / 1
127.43
 getType
33.33% covered (danger)
33.33%
4 / 12
25.00% covered (danger)
25.00%
3 / 12
10.00% covered (danger)
10.00%
1 / 10
0.00% covered (danger)
0.00%
0 / 1
99.21
 convertToDB
50.00% covered (danger)
50.00%
14 / 28
46.88% covered (danger)
46.88%
15 / 32
1.28% covered (danger)
1.28%
1 / 78
0.00% covered (danger)
0.00%
0 / 1
366.29
 nullOrInt
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 getAdditionalFieldNames
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getLocaleFieldNames
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 updateDataObjectSettings
68.75% covered (warning)
68.75%
33 / 48
64.52% covered (warning)
64.52%
20 / 31
0.00% covered (danger)
0.00%
0 / 80
0.00% covered (danger)
0.00%
0 / 1
16.39
 getDataObjectSettings
86.67% covered (warning)
86.67%
13 / 15
80.00% covered (warning)
80.00%
8 / 10
12.50% covered (danger)
12.50%
1 / 8
0.00% covered (danger)
0.00%
0 / 1
14.72
 getDirectionMapping
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
20
 getDataChangedEvent
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 formatDateToDB
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
30
1<?php
2
3/**
4 * @defgroup db DB
5 * Implements basic database concerns such as connection abstraction.
6 */
7
8/**
9 * @file classes/db/DAO.php
10 *
11 * Copyright (c) 2014-2021 Simon Fraser University
12 * Copyright (c) 2000-2021 John Willinsky
13 * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
14 *
15 * @class DAO
16 *
17 * @ingroup db
18 *
19 * @see DAORegistry
20 *
21 * @brief Operations for retrieving and modifying objects from a database.
22 */
23
24namespace PKP\db;
25
26use Generator;
27use Illuminate\Database\Query\Builder;
28use Illuminate\Support\Facades\DB;
29use PKP\core\DataObject;
30use PKP\core\JSONMessage;
31use PKP\plugins\Hook;
32
33class DAO
34{
35    public const SORT_DIRECTION_ASC = 1;
36    public const SORT_DIRECTION_DESC = 2;
37
38    /**
39     * Constructor.
40     * Initialize the database connection.
41     */
42    public function __construct($callHooks = true)
43    {
44        if ($callHooks === true) {
45            // Call hooks based on the object name. Results
46            // in hook calls named e.g. "DAO_CLASS::_Constructor"
47            $classNameParts = explode('\\', get_class($this)); // Separate namespace info from class name
48            if (Hook::run(strtolower(end($classNameParts)) . '::_Constructor', [$this])) {
49                return;
50            }
51        }
52    }
53
54    /**
55     * Execute a SELECT SQL statement.
56     *
57     * @param string $sql the SQL statement
58     * @param array $params parameters for the SQL statement
59     *
60     * @deprecated 3.4
61     *
62     * @return Generator<int,object>
63     */
64    public function retrieve(string $sql, array $params = [], bool $callHooks = true): Generator
65    {
66        if ($callHooks === true) {
67            $trace = debug_backtrace();
68            // Call hooks based on the calling entity, assuming
69            // this method is only called by a subclass. Results
70            // in hook calls named e.g. "DAO_CLASS::_get..."
71            // (always lower case).
72            $value = null;
73            if (Hook::run(strtolower($trace[1]['class'] . '::_' . $trace[1]['function']), [&$sql, &$params, &$value])) {
74                return $value;
75            }
76        }
77
78        return DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
79    }
80
81    /**
82     * Execute a SELECT SQL statement, returning rows in the range supplied.
83     *
84     * @param $sql the SQL statement
85     * @param $params parameters for the SQL statement, params is used only when $sql is a string
86     * @param $dbResultRange object describing the desired range
87     *
88     * @deprecated 3.4
89     */
90    public function retrieveRange(string|Builder $sql, array $params = [], ?DBResultRange $dbResultRange = null, bool $callHooks = true): Iterable
91    {
92        if ($callHooks === true) {
93            $trace = debug_backtrace();
94            // Call hooks based on the calling entity, assuming
95            // this method is only called by a subclass. Results
96            // in hook calls named e.g. "DAO_CLASS::_get..."
97            $value = null;
98            if (Hook::run(strtolower($trace[1]['class'] . '::_' . $trace[1]['function']), [&$sql, &$params, &$dbResultRange, &$value])) {
99                return $value;
100            }
101        }
102
103        if ($dbResultRange && $dbResultRange->isValid()) {
104            $limit = (int) $dbResultRange->getCount();
105            $offset = (int) $dbResultRange->getOffset();
106            $offset += max(0, $dbResultRange->getPage() - 1) * (int) $dbResultRange->getCount();
107            if ($sql instanceof Builder) {
108                $sql->limit($limit)->offset($offset);
109            } else {
110                $sql .= " LIMIT {$limit} OFFSET {$offset}";
111            }
112        }
113
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
115    }
116
117    /**
118     * Count the number of records in the supplied SQL statement (with optional bind parameters parameters)
119     *
120     * @param $sql SQL query to be counted
121     * @param $params Optional SQL query bind parameters, only used when the $sql argument is a string
122     *
123     * @deprecated 3.4
124     */
125    public function countRecords(string|Builder $sql, array $params = []): int
126    {
127        // In case a Laravel Builder has been received, drop its SELECT and ORDER BY clauses for optimization purposes
128        if ($sql instanceof Builder) {
129            return $sql->getCountForPagination();
130        }
131        $result = $this->retrieve('SELECT COUNT(*) AS row_count FROM (' . $sql . ') AS count_subquery', $params);
132        return $result->current()->row_count;
133    }
134
135    /**
136     * Concatenate SQL expressions into a single string.
137     *
138     * @param array ...$args SQL expressions (e.g. column names) to concatenate.
139     *
140     * @deprecated 3.4
141     */
142    public function concat(...$args): string
143    {
144        return 'CONCAT(' . join(',', $args) . ')';
145    }
146
147    /**
148     * Execute an INSERT, UPDATE, or DELETE SQL statement.
149     *
150     * @param $sql the SQL statement the execute
151     * @param $params an array of parameters for the SQL statement
152     * @param $callHooks Whether or not to call hooks
153     * @param $dieOnError Whether or not to die if an error occurs
154     *
155     * @deprecated 3.4
156     *
157     * @return Affected row count
158     */
159    public function update(string $sql, array $params = [], bool $callHooks = true, bool $dieOnError = true): int
160    {
161        if ($callHooks === true) {
162            $trace = debug_backtrace();
163            // Call hooks based on the calling entity, assuming
164            // this method is only called by a subclass. Results
165            // in hook calls named e.g. "DAO_CLASS::_updateobject"
166            // (all lowercase)
167            $value = null;
168            if (Hook::run(strtolower($trace[1]['class'] . '::_' . $trace[1]['function']), [&$sql, &$params, &$value])) {
169                return $value;
170            }
171        }
172
173        return DB::affectingStatement($sql, $params);
174    }
175
176    /**
177     * Insert a row in a table, replacing an existing row if necessary.
178     *
179     * @param $arrFields Associative array of colName => value
180     * @param $keyCols Array of column names that are keys
181     *
182     * @deprecated 3.4
183     */
184    public function replace(string $table, array $arrFields, array $keyCols): void
185    {
186        $matchValues = array_filter($arrFields, fn ($key) => in_array($key, $keyCols), ARRAY_FILTER_USE_KEY);
187        $additionalValues = array_filter($arrFields, fn ($key) => !in_array($key, $keyCols), ARRAY_FILTER_USE_KEY);
188        DB::table($table)->updateOrInsert($matchValues, $additionalValues);
189    }
190
191    /**
192     * Return the last ID inserted in an autonumbered field.
193     */
194    protected function getInsertId(): int
195    {
196        return DB::getPdo()->lastInsertId();
197    }
198
199    /**
200     * Return the last ID inserted in an autonumbered field.
201     *
202     * @deprecated 3.4
203     */
204    public function _getInsertId(): int
205    {
206        return $this->getInsertId();
207    }
208
209    /**
210     * Return datetime formatted for DB insertion.
211     *
212     * @param $dt *nix timestamp or ISO datetime string
213     *
214     * @deprecated 3.4
215     */
216    public function datetimeToDB(null|int|string $dt): string
217    {
218        if ($dt === null) {
219            return 'NULL';
220        }
221        if (!ctype_digit((string) $dt)) {
222            $dt = strtotime($dt);
223        }
224        return '\'' . date('Y-m-d H:i:s', $dt) . '\'';
225    }
226
227    /**
228     * Return date formatted for DB insertion.
229     *
230     * @param $d *nix timestamp or ISO date string
231     *
232     * @deprecated 3.4
233     */
234    public function dateToDB(null|int|string $d): string
235    {
236        if ($d === null) {
237            return 'NULL';
238        }
239        if (!ctype_digit($d)) {
240            $d = strtotime($d);
241        }
242        return '\'' . date('Y-m-d', $d) . '\'';
243    }
244
245    /**
246     * Return datetime from DB as ISO datetime string.
247     *
248     * @deprecated 3.4
249     */
250    public function datetimeFromDB(?string $dt): ?string
251    {
252        return $dt === null ? null : date('Y-m-d H:i:s', strtotime($dt));
253    }
254
255    /**
256     * Return date from DB as ISO date string.
257     *
258     * @deprecated 3.4
259     */
260    public function dateFromDB(?string $d): ?string
261    {
262        return $d === null ? null : date('Y-m-d', strtotime($d));
263    }
264
265    /**
266     * Convert a value from the database to a specific type
267     *
268     * @param $value Value from the database
269     * @param $type Type from the database, eg `string`
270     * @param $nullable True iff the value is allowed to be null
271     */
272    public function convertFromDB(mixed $value, ?string $type, bool $nullable = false): mixed
273    {
274        if ($nullable && $value === null) {
275            return null;
276        }
277        switch ($type) {
278            case 'bool':
279            case 'boolean':
280                return (bool) $value;
281            case 'int':
282            case 'integer':
283                return (int) $value;
284            case 'float':
285            case 'number':
286                return (float) $value;
287            case 'object':
288            case 'array':
289                return json_decode($value, true);
290            case 'date':
291                return strtotime($value);
292            case 'string':
293            default:
294                // Nothing required.
295                break;
296        }
297        return $value;
298    }
299
300    /**
301     * Get the type of a value to be stored in the database
302     *
303     * @deprecated 3.4
304     */
305    public function getType(mixed $value): string
306    {
307        return match(gettype($value)) {
308            'boolean' => 'bool',
309            'bool' => 'bool',
310            'integer' => 'int',
311            'int' => 'int',
312            'double' => 'float',
313            'float' => 'float',
314            'array' => 'object',
315            'object' => 'object',
316            'string' => 'string',
317            default => 'string'
318        };
319    }
320
321    /**
322     * Convert a PHP variable into a string to be stored in the DB
323     *
324     * @param bool $nullable True iff the value is allowed to be null.
325     *
326     * @return string
327     */
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
331            return null;
332        }
333
334        if ($type === null) {
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
340            case 'array':
341                $value = json_encode($value, JSON_UNESCAPED_UNICODE);
342                break;
343            case 'bool':
344            case 'boolean':
345                // Cast to boolean, ensuring that string
346                // "false" evaluates to boolean false
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
349            case 'int':
350            case 'integer':
351                $value = (int) $value;
352                break;
353            case 'float':
354            case 'number':
355                $value = (float) $value;
356                break;
357            case 'date':
358                if ($value !== null) {
359                    if (!is_numeric($value)) {
360                        $value = strtotime($value);
361                    }
362                    $value = date('Y-m-d H:i:s', $value);
363                }
364                break;
365            case 'string':
366            default:
367                // do nothing.
368        }
369
370        return $value;
371    }
372
373    /**
374     * Cast the given parameter to an int, or leave it null.
375     *
376     * @deprecated 3.4
377     */
378    public function nullOrInt(mixed $value): ?int
379    {
380        return (empty($value) ? null : (int) $value);
381    }
382
383    /**
384     * Get a list of additional field names to store in this DAO.
385     * This can be used to extend the table with virtual "columns",
386     * typically using the ..._settings table.
387     *
388     * @deprecated 3.4
389     *
390     * @return array List of strings representing field names.
391     */
392    public function getAdditionalFieldNames(): array
393    {
394        $returner = [];
395        // Call hooks based on the calling entity, assuming
396        // this method is only called by a subclass. Results
397        // in hook calls named e.g. "DAO_CLASS::getAdditionalFieldNames"
398        // (class names lowercase)
399        $classNameParts = explode('\\', get_class($this)); // Separate namespace info from class name
400        Hook::run(strtolower(end($classNameParts)) . '::getAdditionalFieldNames', [$this, &$returner]);
401
402        return $returner;
403    }
404
405    /**
406     * Get locale field names. Like getAdditionalFieldNames, but for
407     * localized (multilingual) fields.
408     *
409     * @see getAdditionalFieldNames
410     * @deprecated 3.4
411     *
412     * @return array Array of string field names.
413     */
414    public function getLocaleFieldNames(): array
415    {
416        $returner = [];
417        // Call hooks based on the calling entity, assuming
418        // this method is only called by a subclass. Results
419        // in hook calls named e.g. "DAO_CLASS::getLocaleFieldNames"
420        // (class names lowercase)
421        $classNameParts = explode('\\', get_class($this)); // Separate namespace info from class name
422        Hook::run(strtolower(end($classNameParts)) . '::getLocaleFieldNames', [$this, &$returner]);
423
424        return $returner;
425    }
426
427    /**
428     * Update the settings table of a data object.
429     *
430     * @deprecated 3.4
431     */
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
521
522    /**
523     * Get contents of the _settings table, storing entries in the specified
524     * data object.
525     *
526     * @param $tableName Settings table name
527     * @param $idFieldName Name of ID column
528     * @param $dataObject Object in which to store retrieved values
529     *
530     * @deprecated 3.4
531     */
532    public function getDataObjectSettings(string $tableName, string $idFieldName, int $idFieldValue, DataObject $dataObject)
533    {
534        if ($idFieldName !== null) {
535            $sql = "SELECT * FROM {$tableName} WHERE {$idFieldName} = ?";
536            $params = [$idFieldValue];
537        } else {
538            $sql = "SELECT * FROM {$tableName}";
539            $params = [];
540        }
541        $result = $this->retrieve($sql, $params);
542        foreach ($result as $row) {
543            $dataObject->setData(
544                $row->setting_name,
545                $this->convertFromDB(
546                    $row->setting_value,
547                    $row->setting_type
548                ),
549                empty($row->locale) ? null : $row->locale
550            );
551        }
552    }
553
554    /**
555     * Get the direction specifier for sorting from a SORT_DIRECTION_... constant.
556     *
557     * @deprecated 3.4
558     */
559    public function getDirectionMapping(int $direction): string
560    {
561        return match($direction) {
562            self::SORT_DIRECTION_ASC => 'ASC',
563            self::SORT_DIRECTION_DESC => 'DESC',
564            default => 'ASC'
565        };
566    }
567
568    /**
569     * Generate a JSON message with an event that can be sent
570     * to the client to refresh itself according to changes
571     * in the DB.
572     *
573     * @param $elementId (Optional) To refresh a single element
574     *  give the element ID here. Otherwise all elements will
575     *  be refreshed.
576     * @param $parentElementId (Optional) To refresh a single
577     *  element that is associated with another one give the parent
578     *  element ID here.
579     * @param $content (Optional) Additional content to pass back
580     *  to the handler of the JSON message.
581     *
582     * @deprecated 3.4
583     */
584    public static function getDataChangedEvent(?string $elementId = null, ?string $parentElementId = null, string $content = ''): JSONMessage
585    {
586        // Create the event data.
587        $eventData = null;
588        if ($elementId) {
589            $eventData = [$elementId];
590            if (strlen($parentElementId ?? '') > 0) {
591                $eventData['parentElementId'] = $parentElementId;
592            }
593        }
594
595        // Create and render the JSON message with the
596        // event to be triggered on the client side.
597        $json = new JSONMessage(true, $content);
598        $json->setEvent('dataChanged', $eventData);
599        return $json;
600    }
601
602    /**
603     * Format a passed date (in English textual datetime)
604     * to Y-m-d H:i:s format, used in database.
605     *
606     * @param string $date Any English textual datetime.
607     * @param int $defaultNumWeeks If passed and date is null,
608     * used to calculate a data in future from today.
609     * @param bool $acceptPastDate Will not accept past dates,
610     * returning today if false and the passed date
611     * is in the past.
612     *
613     * @deprecated 3.4
614     */
615    protected function formatDateToDB(string $date, ?int $defaultNumWeeks = null, bool $acceptPastDate = true): ?string
616    {
617        $today = getDate();
618        $todayTimestamp = mktime(0, 0, 0, $today['mon'], $today['mday'], $today['year']);
619        if ($date != null) {
620            $dateParts = explode('-', $date);
621
622            // If we don't accept past dates...
623            if (!$acceptPastDate && $todayTimestamp > strtotime($date)) {
624                // ... return today.
625                return date('Y-m-d H:i:s', $todayTimestamp);
626            } else {
627                // Return the passed date.
628                return date('Y-m-d H:i:s', mktime(0, 0, 0, $dateParts[1], $dateParts[2], $dateParts[0]));
629            }
630        } elseif (isset($defaultNumWeeks)) {
631            // Add the equivalent of $numWeeks weeks, measured in seconds, to $todaysTimestamp.
632            $numWeeks = max((int) $defaultNumWeeks, 2);
633            $newDueDateTimestamp = $todayTimestamp + ($numWeeks * 7 * 24 * 60 * 60);
634            return date('Y-m-d H:i:s', $newDueDateTimestamp);
635        } else {
636            // Either the date or the defaultNumWeeks must be set
637            assert(false);
638            return null;
639        }
640    }
641}
642
643if (!PKP_STRICT_MODE) {
644    class_alias('\PKP\db\DAO', '\DAO');
645    define('SORT_DIRECTION_ASC', DAO::SORT_DIRECTION_ASC);
646    define('SORT_DIRECTION_DESC', DAO::SORT_DIRECTION_DESC);
647}

Paths

Below are the source code lines that represent each code path as identified by Xdebug. Please note a path is not necessarily coterminous with a line, a line may contain multiple paths and therefore show up more than once. Please also be aware that some paths may include implicit rather than explicit branches, e.g. an if statement always has an else as part of its logical flow even if you didn't write one.

PKP\db\
186        $matchValues = array_filter($arrFields, fn ($key) => in_array($key, $keyCols), ARRAY_FILTER_USE_KEY);
DAO->__construct
42    public function __construct($callHooks = true)
43    {
44        if ($callHooks === true) {
 
47            $classNameParts = explode('\\', get_class($this)); // Separate namespace info from class name
48            if (Hook::run(strtolower(end($classNameParts)) . '::_Constructor', [$this])) {
 
49                return;
42    public function __construct($callHooks = true)
43    {
44        if ($callHooks === true) {
 
47            $classNameParts = explode('\\', get_class($this)); // Separate namespace info from class name
48            if (Hook::run(strtolower(end($classNameParts)) . '::_Constructor', [$this])) {
 
52    }
42    public function __construct($callHooks = true)
43    {
44        if ($callHooks === true) {
 
52    }
DAO->_getInsertId
206        return $this->getInsertId();
207    }
DAO->concat
142    public function concat(...$args): string
143    {
144        return 'CONCAT(' . join(',', $args) . ')';
145    }
DAO->convertFromDB
272    public function convertFromDB(mixed $value, ?string $type, bool $nullable = false): mixed
273    {
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
275            return null;
272    public function convertFromDB(mixed $value, ?string $type, bool $nullable = false): mixed
273    {
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
278            case 'bool':
 
279            case 'boolean':
 
281            case 'int':
 
282            case 'integer':
 
284            case 'float':
 
285            case 'number':
 
287            case 'object':
 
288            case 'array':
 
290            case 'date':
 
292            case 'string':
 
292            case 'string':
 
295                break;
 
297        return $value;
298    }
272    public function convertFromDB(mixed $value, ?string $type, bool $nullable = false): mixed
273    {
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
278            case 'bool':
 
279            case 'boolean':
 
281            case 'int':
 
282            case 'integer':
 
284            case 'float':
 
285            case 'number':
 
287            case 'object':
 
288            case 'array':
 
290            case 'date':
 
292            case 'string':
 
295                break;
 
297        return $value;
298    }
272    public function convertFromDB(mixed $value, ?string $type, bool $nullable = false): mixed
273    {
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
278            case 'bool':
 
279            case 'boolean':
 
281            case 'int':
 
282            case 'integer':
 
284            case 'float':
 
285            case 'number':
 
287            case 'object':
 
288            case 'array':
 
290            case 'date':
 
291                return strtotime($value);
272    public function convertFromDB(mixed $value, ?string $type, bool $nullable = false): mixed
273    {
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
278            case 'bool':
 
279            case 'boolean':
 
281            case 'int':
 
282            case 'integer':
 
284            case 'float':
 
285            case 'number':
 
287            case 'object':
 
288            case 'array':
 
289                return json_decode($value, true);
272    public function convertFromDB(mixed $value, ?string $type, bool $nullable = false): mixed
273    {
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
278            case 'bool':
 
279            case 'boolean':
 
281            case 'int':
 
282            case 'integer':
 
284            case 'float':
 
285            case 'number':
 
287            case 'object':
 
289                return json_decode($value, true);
272    public function convertFromDB(mixed $value, ?string $type, bool $nullable = false): mixed
273    {
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
278            case 'bool':
 
279            case 'boolean':
 
281            case 'int':
 
282            case 'integer':
 
284            case 'float':
 
285            case 'number':
 
286                return (float) $value;
272    public function convertFromDB(mixed $value, ?string $type, bool $nullable = false): mixed
273    {
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
278            case 'bool':
 
279            case 'boolean':
 
281            case 'int':
 
282            case 'integer':
 
284            case 'float':
 
286                return (float) $value;
272    public function convertFromDB(mixed $value, ?string $type, bool $nullable = false): mixed
273    {
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
278            case 'bool':
 
279            case 'boolean':
 
281            case 'int':
 
282            case 'integer':
 
283                return (int) $value;
272    public function convertFromDB(mixed $value, ?string $type, bool $nullable = false): mixed
273    {
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
278            case 'bool':
 
279            case 'boolean':
 
281            case 'int':
 
283                return (int) $value;
272    public function convertFromDB(mixed $value, ?string $type, bool $nullable = false): mixed
273    {
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
278            case 'bool':
 
279            case 'boolean':
 
280                return (bool) $value;
272    public function convertFromDB(mixed $value, ?string $type, bool $nullable = false): mixed
273    {
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
278            case 'bool':
 
280                return (bool) $value;
272    public function convertFromDB(mixed $value, ?string $type, bool $nullable = false): mixed
273    {
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
275            return null;
272    public function convertFromDB(mixed $value, ?string $type, bool $nullable = false): mixed
273    {
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
278            case 'bool':
 
279            case 'boolean':
 
281            case 'int':
 
282            case 'integer':
 
284            case 'float':
 
285            case 'number':
 
287            case 'object':
 
288            case 'array':
 
290            case 'date':
 
292            case 'string':
 
292            case 'string':
 
295                break;
 
297        return $value;
298    }
272    public function convertFromDB(mixed $value, ?string $type, bool $nullable = false): mixed
273    {
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
278            case 'bool':
 
279            case 'boolean':
 
281            case 'int':
 
282            case 'integer':
 
284            case 'float':
 
285            case 'number':
 
287            case 'object':
 
288            case 'array':
 
290            case 'date':
 
292            case 'string':
 
295                break;
 
297        return $value;
298    }
272    public function convertFromDB(mixed $value, ?string $type, bool $nullable = false): mixed
273    {
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
278            case 'bool':
 
279            case 'boolean':
 
281            case 'int':
 
282            case 'integer':
 
284            case 'float':
 
285            case 'number':
 
287            case 'object':
 
288            case 'array':
 
290            case 'date':
 
291                return strtotime($value);
272    public function convertFromDB(mixed $value, ?string $type, bool $nullable = false): mixed
273    {
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
278            case 'bool':
 
279            case 'boolean':
 
281            case 'int':
 
282            case 'integer':
 
284            case 'float':
 
285            case 'number':
 
287            case 'object':
 
288            case 'array':
 
289                return json_decode($value, true);
272    public function convertFromDB(mixed $value, ?string $type, bool $nullable = false): mixed
273    {
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
278            case 'bool':
 
279            case 'boolean':
 
281            case 'int':
 
282            case 'integer':
 
284            case 'float':
 
285            case 'number':
 
287            case 'object':
 
289                return json_decode($value, true);
272    public function convertFromDB(mixed $value, ?string $type, bool $nullable = false): mixed
273    {
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
278            case 'bool':
 
279            case 'boolean':
 
281            case 'int':
 
282            case 'integer':
 
284            case 'float':
 
285            case 'number':
 
286                return (float) $value;
272    public function convertFromDB(mixed $value, ?string $type, bool $nullable = false): mixed
273    {
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
278            case 'bool':
 
279            case 'boolean':
 
281            case 'int':
 
282            case 'integer':
 
284            case 'float':
 
286                return (float) $value;
272    public function convertFromDB(mixed $value, ?string $type, bool $nullable = false): mixed
273    {
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
278            case 'bool':
 
279            case 'boolean':
 
281            case 'int':
 
282            case 'integer':
 
283                return (int) $value;
272    public function convertFromDB(mixed $value, ?string $type, bool $nullable = false): mixed
273    {
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
278            case 'bool':
 
279            case 'boolean':
 
281            case 'int':
 
283                return (int) $value;
272    public function convertFromDB(mixed $value, ?string $type, bool $nullable = false): mixed
273    {
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
278            case 'bool':
 
279            case 'boolean':
 
280                return (bool) $value;
272    public function convertFromDB(mixed $value, ?string $type, bool $nullable = false): mixed
273    {
274        if ($nullable && $value === null) {
 
274        if ($nullable && $value === null) {
 
278            case 'bool':
 
280                return (bool) $value;
DAO->convertToDB
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
331            return null;
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
350            case 'integer':
 
353            case 'float':
 
354            case 'number':
 
357            case 'date':
 
365            case 'string':
 
365            case 'string':
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
350            case 'integer':
 
353            case 'float':
 
354            case 'number':
 
357            case 'date':
 
365            case 'string':
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
350            case 'integer':
 
353            case 'float':
 
354            case 'number':
 
357            case 'date':
 
358                if ($value !== null) {
 
359                    if (!is_numeric($value)) {
 
360                        $value = strtotime($value);
361                    }
362                    $value = date('Y-m-d H:i:s', $value);
 
362                    $value = date('Y-m-d H:i:s', $value);
363                }
364                break;
 
364                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
350            case 'integer':
 
353            case 'float':
 
354            case 'number':
 
357            case 'date':
 
358                if ($value !== null) {
 
359                    if (!is_numeric($value)) {
 
362                    $value = date('Y-m-d H:i:s', $value);
363                }
364                break;
 
364                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
350            case 'integer':
 
353            case 'float':
 
354            case 'number':
 
357            case 'date':
 
358                if ($value !== null) {
 
364                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
350            case 'integer':
 
353            case 'float':
 
354            case 'number':
 
355                $value = (float) $value;
356                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
350            case 'integer':
 
353            case 'float':
 
355                $value = (float) $value;
356                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
350            case 'integer':
 
351                $value = (int) $value;
352                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
351                $value = (int) $value;
352                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
341                $value = json_encode($value, JSON_UNESCAPED_UNICODE);
342                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
341                $value = json_encode($value, JSON_UNESCAPED_UNICODE);
342                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
350            case 'integer':
 
353            case 'float':
 
354            case 'number':
 
357            case 'date':
 
365            case 'string':
 
365            case 'string':
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
350            case 'integer':
 
353            case 'float':
 
354            case 'number':
 
357            case 'date':
 
365            case 'string':
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
350            case 'integer':
 
353            case 'float':
 
354            case 'number':
 
357            case 'date':
 
358                if ($value !== null) {
 
359                    if (!is_numeric($value)) {
 
360                        $value = strtotime($value);
361                    }
362                    $value = date('Y-m-d H:i:s', $value);
 
362                    $value = date('Y-m-d H:i:s', $value);
363                }
364                break;
 
364                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
350            case 'integer':
 
353            case 'float':
 
354            case 'number':
 
357            case 'date':
 
358                if ($value !== null) {
 
359                    if (!is_numeric($value)) {
 
362                    $value = date('Y-m-d H:i:s', $value);
363                }
364                break;
 
364                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
350            case 'integer':
 
353            case 'float':
 
354            case 'number':
 
357            case 'date':
 
358                if ($value !== null) {
 
364                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
350            case 'integer':
 
353            case 'float':
 
354            case 'number':
 
355                $value = (float) $value;
356                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
350            case 'integer':
 
353            case 'float':
 
355                $value = (float) $value;
356                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
350            case 'integer':
 
351                $value = (int) $value;
352                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
351                $value = (int) $value;
352                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
341                $value = json_encode($value, JSON_UNESCAPED_UNICODE);
342                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
341                $value = json_encode($value, JSON_UNESCAPED_UNICODE);
342                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
331            return null;
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
350            case 'integer':
 
353            case 'float':
 
354            case 'number':
 
357            case 'date':
 
365            case 'string':
 
365            case 'string':
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
350            case 'integer':
 
353            case 'float':
 
354            case 'number':
 
357            case 'date':
 
365            case 'string':
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
350            case 'integer':
 
353            case 'float':
 
354            case 'number':
 
357            case 'date':
 
358                if ($value !== null) {
 
359                    if (!is_numeric($value)) {
 
360                        $value = strtotime($value);
361                    }
362                    $value = date('Y-m-d H:i:s', $value);
 
362                    $value = date('Y-m-d H:i:s', $value);
363                }
364                break;
 
364                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
350            case 'integer':
 
353            case 'float':
 
354            case 'number':
 
357            case 'date':
 
358                if ($value !== null) {
 
359                    if (!is_numeric($value)) {
 
362                    $value = date('Y-m-d H:i:s', $value);
363                }
364                break;
 
364                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
350            case 'integer':
 
353            case 'float':
 
354            case 'number':
 
357            case 'date':
 
358                if ($value !== null) {
 
364                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
350            case 'integer':
 
353            case 'float':
 
354            case 'number':
 
355                $value = (float) $value;
356                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
350            case 'integer':
 
353            case 'float':
 
355                $value = (float) $value;
356                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
350            case 'integer':
 
351                $value = (int) $value;
352                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
351                $value = (int) $value;
352                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
340            case 'array':
 
341                $value = json_encode($value, JSON_UNESCAPED_UNICODE);
342                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
335            $type = $this->getType($value);
336        }
337
338        switch ($type) {
339            case 'object':
 
339            case 'object':
 
341                $value = json_encode($value, JSON_UNESCAPED_UNICODE);
342                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
350            case 'integer':
 
353            case 'float':
 
354            case 'number':
 
357            case 'date':
 
365            case 'string':
 
365            case 'string':
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
350            case 'integer':
 
353            case 'float':
 
354            case 'number':
 
357            case 'date':
 
365            case 'string':
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
350            case 'integer':
 
353            case 'float':
 
354            case 'number':
 
357            case 'date':
 
358                if ($value !== null) {
 
359                    if (!is_numeric($value)) {
 
360                        $value = strtotime($value);
361                    }
362                    $value = date('Y-m-d H:i:s', $value);
 
362                    $value = date('Y-m-d H:i:s', $value);
363                }
364                break;
 
364                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
350            case 'integer':
 
353            case 'float':
 
354            case 'number':
 
357            case 'date':
 
358                if ($value !== null) {
 
359                    if (!is_numeric($value)) {
 
362                    $value = date('Y-m-d H:i:s', $value);
363                }
364                break;
 
364                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
350            case 'integer':
 
353            case 'float':
 
354            case 'number':
 
357            case 'date':
 
358                if ($value !== null) {
 
364                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
350            case 'integer':
 
353            case 'float':
 
354            case 'number':
 
355                $value = (float) $value;
356                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
350            case 'integer':
 
353            case 'float':
 
355                $value = (float) $value;
356                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
350            case 'integer':
 
351                $value = (int) $value;
352                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
349            case 'int':
 
351                $value = (int) $value;
352                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
344            case 'boolean':
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
343            case 'bool':
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
 
347                $value = ($value && $value !== 'false') ? 1 : 0;
348                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
340            case 'array':
 
341                $value = json_encode($value, JSON_UNESCAPED_UNICODE);
342                break;
 
370        return $value;
371    }
328    public function convertToDB(mixed $value, ?string &$type = null, bool $nullable = false)
329    {
330        if ($nullable && $value === null) {
 
330        if ($nullable && $value === null) {
 
334        if ($type === null) {
 
339            case 'object':
 
341                $value = json_encode($value, JSON_UNESCAPED_UNICODE);
342                break;
 
370        return $value;
371    }
DAO->countRecords
125    public function countRecords(string|Builder $sql, array $params = []): int
126    {
127        // In case a Laravel Builder has been received, drop its SELECT and ORDER BY clauses for optimization purposes
128        if ($sql instanceof Builder) {
 
129            return $sql->getCountForPagination();
125    public function countRecords(string|Builder $sql, array $params = []): int
126    {
127        // In case a Laravel Builder has been received, drop its SELECT and ORDER BY clauses for optimization purposes
128        if ($sql instanceof Builder) {
 
131        $result = $this->retrieve('SELECT COUNT(*) AS row_count FROM (' . $sql . ') AS count_subquery', $params);
132        return $result->current()->row_count;
133    }
DAO->dateFromDB
260    public function dateFromDB(?string $d): ?string
261    {
262        return $d === null ? null : date('Y-m-d', strtotime($d));
 
262        return $d === null ? null : date('Y-m-d', strtotime($d));
 
262        return $d === null ? null : date('Y-m-d', strtotime($d));
263    }
260    public function dateFromDB(?string $d): ?string
261    {
262        return $d === null ? null : date('Y-m-d', strtotime($d));
 
262        return $d === null ? null : date('Y-m-d', strtotime($d));
 
262        return $d === null ? null : date('Y-m-d', strtotime($d));
263    }
DAO->dateToDB
234    public function dateToDB(null|int|string $d): string
235    {
236        if ($d === null) {
 
237            return 'NULL';
234    public function dateToDB(null|int|string $d): string
235    {
236        if ($d === null) {
 
239        if (!ctype_digit($d)) {
 
240            $d = strtotime($d);
241        }
242        return '\'' . date('Y-m-d', $d) . '\'';
 
242        return '\'' . date('Y-m-d', $d) . '\'';
243    }
234    public function dateToDB(null|int|string $d): string
235    {
236        if ($d === null) {
 
239        if (!ctype_digit($d)) {
 
242        return '\'' . date('Y-m-d', $d) . '\'';
243    }
DAO->datetimeFromDB
250    public function datetimeFromDB(?string $dt): ?string
251    {
252        return $dt === null ? null : date('Y-m-d H:i:s', strtotime($dt));
 
252        return $dt === null ? null : date('Y-m-d H:i:s', strtotime($dt));
 
252        return $dt === null ? null : date('Y-m-d H:i:s', strtotime($dt));
253    }
250    public function datetimeFromDB(?string $dt): ?string
251    {
252        return $dt === null ? null : date('Y-m-d H:i:s', strtotime($dt));
 
252        return $dt === null ? null : date('Y-m-d H:i:s', strtotime($dt));
 
252        return $dt === null ? null : date('Y-m-d H:i:s', strtotime($dt));
253    }
DAO->datetimeToDB
216    public function datetimeToDB(null|int|string $dt): string
217    {
218        if ($dt === null) {
 
219            return 'NULL';
216    public function datetimeToDB(null|int|string $dt): string
217    {
218        if ($dt === null) {
 
221        if (!ctype_digit((string) $dt)) {
 
222            $dt = strtotime($dt);
223        }
224        return '\'' . date('Y-m-d H:i:s', $dt) . '\'';
 
224        return '\'' . date('Y-m-d H:i:s', $dt) . '\'';
225    }
216    public function datetimeToDB(null|int|string $dt): string
217    {
218        if ($dt === null) {
 
221        if (!ctype_digit((string) $dt)) {
 
224        return '\'' . date('Y-m-d H:i:s', $dt) . '\'';
225    }
DAO->formatDateToDB
615    protected function formatDateToDB(string $date, ?int $defaultNumWeeks = null, bool $acceptPastDate = true): ?string
616    {
617        $today = getDate();
618        $todayTimestamp = mktime(0, 0, 0, $today['mon'], $today['mday'], $today['year']);
619        if ($date != null) {
 
620            $dateParts = explode('-', $date);
621
622            // If we don't accept past dates...
623            if (!$acceptPastDate && $todayTimestamp > strtotime($date)) {
 
623            if (!$acceptPastDate && $todayTimestamp > strtotime($date)) {
 
623            if (!$acceptPastDate && $todayTimestamp > strtotime($date)) {
 
625                return date('Y-m-d H:i:s', $todayTimestamp);
615    protected function formatDateToDB(string $date, ?int $defaultNumWeeks = null, bool $acceptPastDate = true): ?string
616    {
617        $today = getDate();
618        $todayTimestamp = mktime(0, 0, 0, $today['mon'], $today['mday'], $today['year']);
619        if ($date != null) {
 
620            $dateParts = explode('-', $date);
621
622            // If we don't accept past dates...
623            if (!$acceptPastDate && $todayTimestamp > strtotime($date)) {
 
623            if (!$acceptPastDate && $todayTimestamp > strtotime($date)) {
 
623            if (!$acceptPastDate && $todayTimestamp > strtotime($date)) {
 
628                return date('Y-m-d H:i:s', mktime(0, 0, 0, $dateParts[1], $dateParts[2], $dateParts[0]));
615    protected function formatDateToDB(string $date, ?int $defaultNumWeeks = null, bool $acceptPastDate = true): ?string
616    {
617        $today = getDate();
618        $todayTimestamp = mktime(0, 0, 0, $today['mon'], $today['mday'], $today['year']);
619        if ($date != null) {
 
620            $dateParts = explode('-', $date);
621
622            // If we don't accept past dates...
623            if (!$acceptPastDate && $todayTimestamp > strtotime($date)) {
 
623            if (!$acceptPastDate && $todayTimestamp > strtotime($date)) {
 
625                return date('Y-m-d H:i:s', $todayTimestamp);
615    protected function formatDateToDB(string $date, ?int $defaultNumWeeks = null, bool $acceptPastDate = true): ?string
616    {
617        $today = getDate();
618        $todayTimestamp = mktime(0, 0, 0, $today['mon'], $today['mday'], $today['year']);
619        if ($date != null) {
 
620            $dateParts = explode('-', $date);
621
622            // If we don't accept past dates...
623            if (!$acceptPastDate && $todayTimestamp > strtotime($date)) {
 
623            if (!$acceptPastDate && $todayTimestamp > strtotime($date)) {
 
628                return date('Y-m-d H:i:s', mktime(0, 0, 0, $dateParts[1], $dateParts[2], $dateParts[0]));
615    protected function formatDateToDB(string $date, ?int $defaultNumWeeks = null, bool $acceptPastDate = true): ?string
616    {
617        $today = getDate();
618        $todayTimestamp = mktime(0, 0, 0, $today['mon'], $today['mday'], $today['year']);
619        if ($date != null) {
 
630        } elseif (isset($defaultNumWeeks)) {
 
632            $numWeeks = max((int) $defaultNumWeeks, 2);
633            $newDueDateTimestamp = $todayTimestamp + ($numWeeks * 7 * 24 * 60 * 60);
634            return date('Y-m-d H:i:s', $newDueDateTimestamp);
615    protected function formatDateToDB(string $date, ?int $defaultNumWeeks = null, bool $acceptPastDate = true): ?string
616    {
617        $today = getDate();
618        $todayTimestamp = mktime(0, 0, 0, $today['mon'], $today['mday'], $today['year']);
619        if ($date != null) {
 
630        } elseif (isset($defaultNumWeeks)) {
 
637            assert(false);
638            return null;
639        }
640    }
DAO->getAdditionalFieldNames
394        $returner = [];
395        // Call hooks based on the calling entity, assuming
396        // this method is only called by a subclass. Results
397        // in hook calls named e.g. "DAO_CLASS::getAdditionalFieldNames"
398        // (class names lowercase)
399        $classNameParts = explode('\\', get_class($this)); // Separate namespace info from class name
400        Hook::run(strtolower(end($classNameParts)) . '::getAdditionalFieldNames', [$this, &$returner]);
401
402        return $returner;
403    }
DAO->getDataChangedEvent
584    public static function getDataChangedEvent(?string $elementId = null, ?string $parentElementId = null, string $content = ''): JSONMessage
585    {
586        // Create the event data.
587        $eventData = null;
588        if ($elementId) {
 
589            $eventData = [$elementId];
590            if (strlen($parentElementId ?? '') > 0) {
 
591                $eventData['parentElementId'] = $parentElementId;
592            }
593        }
594
595        // Create and render the JSON message with the
596        // event to be triggered on the client side.
597        $json = new JSONMessage(true, $content);
 
597        $json = new JSONMessage(true, $content);
598        $json->setEvent('dataChanged', $eventData);
599        return $json;
600    }
584    public static function getDataChangedEvent(?string $elementId = null, ?string $parentElementId = null, string $content = ''): JSONMessage
585    {
586        // Create the event data.
587        $eventData = null;
588        if ($elementId) {
 
589            $eventData = [$elementId];
590            if (strlen($parentElementId ?? '') > 0) {
 
597        $json = new JSONMessage(true, $content);
598        $json->setEvent('dataChanged', $eventData);
599        return $json;
600    }
584    public static function getDataChangedEvent(?string $elementId = null, ?string $parentElementId = null, string $content = ''): JSONMessage
585    {
586        // Create the event data.
587        $eventData = null;
588        if ($elementId) {
 
597        $json = new JSONMessage(true, $content);
598        $json->setEvent('dataChanged', $eventData);
599        return $json;
600    }
DAO->getDataObjectSettings
532    public function getDataObjectSettings(string $tableName, string $idFieldName, int $idFieldValue, DataObject $dataObject)
533    {
534        if ($idFieldName !== null) {
 
534        if ($idFieldName !== null) {
535            $sql = "SELECT * FROM {$tableName} WHERE {$idFieldName} = ?";
 
541        $result = $this->retrieve($sql, $params);
542        foreach ($result as $row) {
 
542        foreach ($result as $row) {
 
543            $dataObject->setData(
544                $row->setting_name,
545                $this->convertFromDB(
546                    $row->setting_value,
547                    $row->setting_type
548                ),
549                empty($row->locale) ? null : $row->locale
 
549                empty($row->locale) ? null : $row->locale
 
542        foreach ($result as $row) {
543            $dataObject->setData(
544                $row->setting_name,
545                $this->convertFromDB(
546                    $row->setting_value,
547                    $row->setting_type
548                ),
549                empty($row->locale) ? null : $row->locale
 
542        foreach ($result as $row) {
 
542        foreach ($result as $row) {
543            $dataObject->setData(
544                $row->setting_name,
545                $this->convertFromDB(
546                    $row->setting_value,
547                    $row->setting_type
548                ),
549                empty($row->locale) ? null : $row->locale
550            );
551        }
552    }
532    public function getDataObjectSettings(string $tableName, string $idFieldName, int $idFieldValue, DataObject $dataObject)
533    {
534        if ($idFieldName !== null) {
 
534        if ($idFieldName !== null) {
535            $sql = "SELECT * FROM {$tableName} WHERE {$idFieldName} = ?";
 
541        $result = $this->retrieve($sql, $params);
542        foreach ($result as $row) {
 
542        foreach ($result as $row) {
 
543            $dataObject->setData(
544                $row->setting_name,
545                $this->convertFromDB(
546                    $row->setting_value,
547                    $row->setting_type
548                ),
549                empty($row->locale) ? null : $row->locale
 
549                empty($row->locale) ? null : $row->locale
 
542        foreach ($result as $row) {
543            $dataObject->setData(
544                $row->setting_name,
545                $this->convertFromDB(
546                    $row->setting_value,
547                    $row->setting_type
548                ),
549                empty($row->locale) ? null : $row->locale
 
542        foreach ($result as $row) {
 
542        foreach ($result as $row) {
543            $dataObject->setData(
544                $row->setting_name,
545                $this->convertFromDB(
546                    $row->setting_value,
547                    $row->setting_type
548                ),
549                empty($row->locale) ? null : $row->locale
550            );
551        }
552    }
532    public function getDataObjectSettings(string $tableName, string $idFieldName, int $idFieldValue, DataObject $dataObject)
533    {
534        if ($idFieldName !== null) {
 
534        if ($idFieldName !== null) {
535            $sql = "SELECT * FROM {$tableName} WHERE {$idFieldName} = ?";
 
541        $result = $this->retrieve($sql, $params);
542        foreach ($result as $row) {
 
542        foreach ($result as $row) {
 
542        foreach ($result as $row) {
543            $dataObject->setData(
544                $row->setting_name,
545                $this->convertFromDB(
546                    $row->setting_value,
547                    $row->setting_type
548                ),
549                empty($row->locale) ? null : $row->locale
550            );
551        }
552    }
532    public function getDataObjectSettings(string $tableName, string $idFieldName, int $idFieldValue, DataObject $dataObject)
533    {
534        if ($idFieldName !== null) {
 
534        if ($idFieldName !== null) {
535            $sql = "SELECT * FROM {$tableName} WHERE {$idFieldName} = ?";
 
541        $result = $this->retrieve($sql, $params);
542        foreach ($result as $row) {
 
542        foreach ($result as $row) {
543            $dataObject->setData(
544                $row->setting_name,
545                $this->convertFromDB(
546                    $row->setting_value,
547                    $row->setting_type
548                ),
549                empty($row->locale) ? null : $row->locale
550            );
551        }
552    }
532    public function getDataObjectSettings(string $tableName, string $idFieldName, int $idFieldValue, DataObject $dataObject)
533    {
534        if ($idFieldName !== null) {
 
538            $sql = "SELECT * FROM {$tableName}";
539            $params = [];
540        }
541        $result = $this->retrieve($sql, $params);
 
541        $result = $this->retrieve($sql, $params);
542        foreach ($result as $row) {
 
542        foreach ($result as $row) {
 
543            $dataObject->setData(
544                $row->setting_name,
545                $this->convertFromDB(
546                    $row->setting_value,
547                    $row->setting_type
548                ),
549                empty($row->locale) ? null : $row->locale
 
549                empty($row->locale) ? null : $row->locale
 
542        foreach ($result as $row) {
543            $dataObject->setData(
544                $row->setting_name,
545                $this->convertFromDB(
546                    $row->setting_value,
547                    $row->setting_type
548                ),
549                empty($row->locale) ? null : $row->locale
 
542        foreach ($result as $row) {
 
542        foreach ($result as $row) {
543            $dataObject->setData(
544                $row->setting_name,
545                $this->convertFromDB(
546                    $row->setting_value,
547                    $row->setting_type
548                ),
549                empty($row->locale) ? null : $row->locale
550            );
551        }
552    }
532    public function getDataObjectSettings(string $tableName, string $idFieldName, int $idFieldValue, DataObject $dataObject)
533    {
534        if ($idFieldName !== null) {
 
538            $sql = "SELECT * FROM {$tableName}";
539            $params = [];
540        }
541        $result = $this->retrieve($sql, $params);
 
541        $result = $this->retrieve($sql, $params);
542        foreach ($result as $row) {
 
542        foreach ($result as $row) {
 
543            $dataObject->setData(
544                $row->setting_name,
545                $this->convertFromDB(
546                    $row->setting_value,
547                    $row->setting_type
548                ),
549                empty($row->locale) ? null : $row->locale
 
549                empty($row->locale) ? null : $row->locale
 
542        foreach ($result as $row) {
543            $dataObject->setData(
544                $row->setting_name,
545                $this->convertFromDB(
546                    $row->setting_value,
547                    $row->setting_type
548                ),
549                empty($row->locale) ? null : $row->locale
 
542        foreach ($result as $row) {
 
542        foreach ($result as $row) {
543            $dataObject->setData(
544                $row->setting_name,
545                $this->convertFromDB(
546                    $row->setting_value,
547                    $row->setting_type
548                ),
549                empty($row->locale) ? null : $row->locale
550            );
551        }
552    }
532    public function getDataObjectSettings(string $tableName, string $idFieldName, int $idFieldValue, DataObject $dataObject)
533    {
534        if ($idFieldName !== null) {
 
538            $sql = "SELECT * FROM {$tableName}";
539            $params = [];
540        }
541        $result = $this->retrieve($sql, $params);
 
541        $result = $this->retrieve($sql, $params);
542        foreach ($result as $row) {
 
542        foreach ($result as $row) {
 
542        foreach ($result as $row) {
543            $dataObject->setData(
544                $row->setting_name,
545                $this->convertFromDB(
546                    $row->setting_value,
547                    $row->setting_type
548                ),
549                empty($row->locale) ? null : $row->locale
550            );
551        }
552    }
532    public function getDataObjectSettings(string $tableName, string $idFieldName, int $idFieldValue, DataObject $dataObject)
533    {
534        if ($idFieldName !== null) {
 
538            $sql = "SELECT * FROM {$tableName}";
539            $params = [];
540        }
541        $result = $this->retrieve($sql, $params);
 
541        $result = $this->retrieve($sql, $params);
542        foreach ($result as $row) {
 
542        foreach ($result as $row) {
543            $dataObject->setData(
544                $row->setting_name,
545                $this->convertFromDB(
546                    $row->setting_value,
547                    $row->setting_type
548                ),
549                empty($row->locale) ? null : $row->locale
550            );
551        }
552    }
DAO->getDirectionMapping
559    public function getDirectionMapping(int $direction): string
560    {
561        return match($direction) {
 
562            self::SORT_DIRECTION_ASC => 'ASC',
 
564            default => 'ASC'
565        };
566    }
559    public function getDirectionMapping(int $direction): string
560    {
561        return match($direction) {
 
563            self::SORT_DIRECTION_DESC => 'DESC',
 
564            default => 'ASC'
565        };
566    }
559    public function getDirectionMapping(int $direction): string
560    {
561        return match($direction) {
 
564            default => 'ASC'
 
564            default => 'ASC'
565        };
566    }
DAO->getInsertId
196        return DB::getPdo()->lastInsertId();
197    }
DAO->getLocaleFieldNames
416        $returner = [];
417        // Call hooks based on the calling entity, assuming
418        // this method is only called by a subclass. Results
419        // in hook calls named e.g. "DAO_CLASS::getLocaleFieldNames"
420        // (class names lowercase)
421        $classNameParts = explode('\\', get_class($this)); // Separate namespace info from class name
422        Hook::run(strtolower(end($classNameParts)) . '::getLocaleFieldNames', [$this, &$returner]);
423
424        return $returner;
425    }
DAO->getType
305    public function getType(mixed $value): string
306    {
307        return match(gettype($value)) {
 
308            'boolean' => 'bool',
 
317            default => 'string'
318        };
319    }
305    public function getType(mixed $value): string
306    {
307        return match(gettype($value)) {
 
309            'bool' => 'bool',
 
317            default => 'string'
318        };
319    }
305    public function getType(mixed $value): string
306    {
307        return match(gettype($value)) {
 
310            'integer' => 'int',
 
317            default => 'string'
318        };
319    }
305    public function getType(mixed $value): string
306    {
307        return match(gettype($value)) {
 
311            'int' => 'int',
 
317            default => 'string'
318        };
319    }
305    public function getType(mixed $value): string
306    {
307        return match(gettype($value)) {
 
312            'double' => 'float',
 
317            default => 'string'
318        };
319    }
305    public function getType(mixed $value): string
306    {
307        return match(gettype($value)) {
 
313            'float' => 'float',
 
317            default => 'string'
318        };
319    }
305    public function getType(mixed $value): string
306    {
307        return match(gettype($value)) {
 
314            'array' => 'object',
 
317            default => 'string'
318        };
319    }
305    public function getType(mixed $value): string
306    {
307        return match(gettype($value)) {
 
315            'object' => 'object',
 
317            default => 'string'
318        };
319    }
305    public function getType(mixed $value): string
306    {
307        return match(gettype($value)) {
 
316            'string' => 'string',
 
317            default => 'string'
318        };
319    }
305    public function getType(mixed $value): string
306    {
307        return match(gettype($value)) {
 
317            default => 'string'
 
317            default => 'string'
318        };
319    }
DAO->nullOrInt
378    public function nullOrInt(mixed $value): ?int
379    {
380        return (empty($value) ? null : (int) $value);
 
380        return (empty($value) ? null : (int) $value);
 
380        return (empty($value) ? null : (int) $value);
381    }
378    public function nullOrInt(mixed $value): ?int
379    {
380        return (empty($value) ? null : (int) $value);
 
380        return (empty($value) ? null : (int) $value);
 
380        return (empty($value) ? null : (int) $value);
381    }
DAO->replace
184    public function replace(string $table, array $arrFields, array $keyCols): void
185    {
186        $matchValues = array_filter($arrFields, fn ($key) => in_array($key, $keyCols), ARRAY_FILTER_USE_KEY);
187        $additionalValues = array_filter($arrFields, fn ($key) => !in_array($key, $keyCols), ARRAY_FILTER_USE_KEY);
188        DB::table($table)->updateOrInsert($matchValues, $additionalValues);
189    }
DAO->retrieve
64    public function retrieve(string $sql, array $params = [], bool $callHooks = true): Generator
65    {
66        if ($callHooks === true) {
 
67            $trace = debug_backtrace();
68            // Call hooks based on the calling entity, assuming
69            // this method is only called by a subclass. Results
70            // in hook calls named e.g. "DAO_CLASS::_get..."
71            // (always lower case).
72            $value = null;
73            if (Hook::run(strtolower($trace[1]['class'] . '::_' . $trace[1]['function']), [&$sql, &$params, &$value])) {
 
74                return $value;
64    public function retrieve(string $sql, array $params = [], bool $callHooks = true): Generator
65    {
66        if ($callHooks === true) {
 
67            $trace = debug_backtrace();
68            // Call hooks based on the calling entity, assuming
69            // this method is only called by a subclass. Results
70            // in hook calls named e.g. "DAO_CLASS::_get..."
71            // (always lower case).
72            $value = null;
73            if (Hook::run(strtolower($trace[1]['class'] . '::_' . $trace[1]['function']), [&$sql, &$params, &$value])) {
 
78        return DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
79    }
64    public function retrieve(string $sql, array $params = [], bool $callHooks = true): Generator
65    {
66        if ($callHooks === true) {
 
78        return DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
79    }
DAO->retrieveRange
90    public function retrieveRange(string|Builder $sql, array $params = [], ?DBResultRange $dbResultRange = null, bool $callHooks = true): Iterable
91    {
92        if ($callHooks === true) {
 
93            $trace = debug_backtrace();
94            // Call hooks based on the calling entity, assuming
95            // this method is only called by a subclass. Results
96            // in hook calls named e.g. "DAO_CLASS::_get..."
97            $value = null;
98            if (Hook::run(strtolower($trace[1]['class'] . '::_' . $trace[1]['function']), [&$sql, &$params, &$dbResultRange, &$value])) {
 
99                return $value;
90    public function retrieveRange(string|Builder $sql, array $params = [], ?DBResultRange $dbResultRange = null, bool $callHooks = true): Iterable
91    {
92        if ($callHooks === true) {
 
93            $trace = debug_backtrace();
94            // Call hooks based on the calling entity, assuming
95            // this method is only called by a subclass. Results
96            // in hook calls named e.g. "DAO_CLASS::_get..."
97            $value = null;
98            if (Hook::run(strtolower($trace[1]['class'] . '::_' . $trace[1]['function']), [&$sql, &$params, &$dbResultRange, &$value])) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
104            $limit = (int) $dbResultRange->getCount();
105            $offset = (int) $dbResultRange->getOffset();
106            $offset += max(0, $dbResultRange->getPage() - 1) * (int) $dbResultRange->getCount();
107            if ($sql instanceof Builder) {
 
107            if ($sql instanceof Builder) {
108                $sql->limit($limit)->offset($offset);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
115    }
90    public function retrieveRange(string|Builder $sql, array $params = [], ?DBResultRange $dbResultRange = null, bool $callHooks = true): Iterable
91    {
92        if ($callHooks === true) {
 
93            $trace = debug_backtrace();
94            // Call hooks based on the calling entity, assuming
95            // this method is only called by a subclass. Results
96            // in hook calls named e.g. "DAO_CLASS::_get..."
97            $value = null;
98            if (Hook::run(strtolower($trace[1]['class'] . '::_' . $trace[1]['function']), [&$sql, &$params, &$dbResultRange, &$value])) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
104            $limit = (int) $dbResultRange->getCount();
105            $offset = (int) $dbResultRange->getOffset();
106            $offset += max(0, $dbResultRange->getPage() - 1) * (int) $dbResultRange->getCount();
107            if ($sql instanceof Builder) {
 
107            if ($sql instanceof Builder) {
108                $sql->limit($limit)->offset($offset);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
115    }
90    public function retrieveRange(string|Builder $sql, array $params = [], ?DBResultRange $dbResultRange = null, bool $callHooks = true): Iterable
91    {
92        if ($callHooks === true) {
 
93            $trace = debug_backtrace();
94            // Call hooks based on the calling entity, assuming
95            // this method is only called by a subclass. Results
96            // in hook calls named e.g. "DAO_CLASS::_get..."
97            $value = null;
98            if (Hook::run(strtolower($trace[1]['class'] . '::_' . $trace[1]['function']), [&$sql, &$params, &$dbResultRange, &$value])) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
104            $limit = (int) $dbResultRange->getCount();
105            $offset = (int) $dbResultRange->getOffset();
106            $offset += max(0, $dbResultRange->getPage() - 1) * (int) $dbResultRange->getCount();
107            if ($sql instanceof Builder) {
 
110                $sql .= " LIMIT {$limit} OFFSET {$offset}";
111            }
112        }
113
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
115    }
90    public function retrieveRange(string|Builder $sql, array $params = [], ?DBResultRange $dbResultRange = null, bool $callHooks = true): Iterable
91    {
92        if ($callHooks === true) {
 
93            $trace = debug_backtrace();
94            // Call hooks based on the calling entity, assuming
95            // this method is only called by a subclass. Results
96            // in hook calls named e.g. "DAO_CLASS::_get..."
97            $value = null;
98            if (Hook::run(strtolower($trace[1]['class'] . '::_' . $trace[1]['function']), [&$sql, &$params, &$dbResultRange, &$value])) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
104            $limit = (int) $dbResultRange->getCount();
105            $offset = (int) $dbResultRange->getOffset();
106            $offset += max(0, $dbResultRange->getPage() - 1) * (int) $dbResultRange->getCount();
107            if ($sql instanceof Builder) {
 
110                $sql .= " LIMIT {$limit} OFFSET {$offset}";
111            }
112        }
113
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
115    }
90    public function retrieveRange(string|Builder $sql, array $params = [], ?DBResultRange $dbResultRange = null, bool $callHooks = true): Iterable
91    {
92        if ($callHooks === true) {
 
93            $trace = debug_backtrace();
94            // Call hooks based on the calling entity, assuming
95            // this method is only called by a subclass. Results
96            // in hook calls named e.g. "DAO_CLASS::_get..."
97            $value = null;
98            if (Hook::run(strtolower($trace[1]['class'] . '::_' . $trace[1]['function']), [&$sql, &$params, &$dbResultRange, &$value])) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
115    }
90    public function retrieveRange(string|Builder $sql, array $params = [], ?DBResultRange $dbResultRange = null, bool $callHooks = true): Iterable
91    {
92        if ($callHooks === true) {
 
93            $trace = debug_backtrace();
94            // Call hooks based on the calling entity, assuming
95            // this method is only called by a subclass. Results
96            // in hook calls named e.g. "DAO_CLASS::_get..."
97            $value = null;
98            if (Hook::run(strtolower($trace[1]['class'] . '::_' . $trace[1]['function']), [&$sql, &$params, &$dbResultRange, &$value])) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
115    }
90    public function retrieveRange(string|Builder $sql, array $params = [], ?DBResultRange $dbResultRange = null, bool $callHooks = true): Iterable
91    {
92        if ($callHooks === true) {
 
93            $trace = debug_backtrace();
94            // Call hooks based on the calling entity, assuming
95            // this method is only called by a subclass. Results
96            // in hook calls named e.g. "DAO_CLASS::_get..."
97            $value = null;
98            if (Hook::run(strtolower($trace[1]['class'] . '::_' . $trace[1]['function']), [&$sql, &$params, &$dbResultRange, &$value])) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
104            $limit = (int) $dbResultRange->getCount();
105            $offset = (int) $dbResultRange->getOffset();
106            $offset += max(0, $dbResultRange->getPage() - 1) * (int) $dbResultRange->getCount();
107            if ($sql instanceof Builder) {
 
107            if ($sql instanceof Builder) {
108                $sql->limit($limit)->offset($offset);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
115    }
90    public function retrieveRange(string|Builder $sql, array $params = [], ?DBResultRange $dbResultRange = null, bool $callHooks = true): Iterable
91    {
92        if ($callHooks === true) {
 
93            $trace = debug_backtrace();
94            // Call hooks based on the calling entity, assuming
95            // this method is only called by a subclass. Results
96            // in hook calls named e.g. "DAO_CLASS::_get..."
97            $value = null;
98            if (Hook::run(strtolower($trace[1]['class'] . '::_' . $trace[1]['function']), [&$sql, &$params, &$dbResultRange, &$value])) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
104            $limit = (int) $dbResultRange->getCount();
105            $offset = (int) $dbResultRange->getOffset();
106            $offset += max(0, $dbResultRange->getPage() - 1) * (int) $dbResultRange->getCount();
107            if ($sql instanceof Builder) {
 
107            if ($sql instanceof Builder) {
108                $sql->limit($limit)->offset($offset);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
115    }
90    public function retrieveRange(string|Builder $sql, array $params = [], ?DBResultRange $dbResultRange = null, bool $callHooks = true): Iterable
91    {
92        if ($callHooks === true) {
 
93            $trace = debug_backtrace();
94            // Call hooks based on the calling entity, assuming
95            // this method is only called by a subclass. Results
96            // in hook calls named e.g. "DAO_CLASS::_get..."
97            $value = null;
98            if (Hook::run(strtolower($trace[1]['class'] . '::_' . $trace[1]['function']), [&$sql, &$params, &$dbResultRange, &$value])) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
104            $limit = (int) $dbResultRange->getCount();
105            $offset = (int) $dbResultRange->getOffset();
106            $offset += max(0, $dbResultRange->getPage() - 1) * (int) $dbResultRange->getCount();
107            if ($sql instanceof Builder) {
 
110                $sql .= " LIMIT {$limit} OFFSET {$offset}";
111            }
112        }
113
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
115    }
90    public function retrieveRange(string|Builder $sql, array $params = [], ?DBResultRange $dbResultRange = null, bool $callHooks = true): Iterable
91    {
92        if ($callHooks === true) {
 
93            $trace = debug_backtrace();
94            // Call hooks based on the calling entity, assuming
95            // this method is only called by a subclass. Results
96            // in hook calls named e.g. "DAO_CLASS::_get..."
97            $value = null;
98            if (Hook::run(strtolower($trace[1]['class'] . '::_' . $trace[1]['function']), [&$sql, &$params, &$dbResultRange, &$value])) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
104            $limit = (int) $dbResultRange->getCount();
105            $offset = (int) $dbResultRange->getOffset();
106            $offset += max(0, $dbResultRange->getPage() - 1) * (int) $dbResultRange->getCount();
107            if ($sql instanceof Builder) {
 
110                $sql .= " LIMIT {$limit} OFFSET {$offset}";
111            }
112        }
113
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
115    }
90    public function retrieveRange(string|Builder $sql, array $params = [], ?DBResultRange $dbResultRange = null, bool $callHooks = true): Iterable
91    {
92        if ($callHooks === true) {
 
93            $trace = debug_backtrace();
94            // Call hooks based on the calling entity, assuming
95            // this method is only called by a subclass. Results
96            // in hook calls named e.g. "DAO_CLASS::_get..."
97            $value = null;
98            if (Hook::run(strtolower($trace[1]['class'] . '::_' . $trace[1]['function']), [&$sql, &$params, &$dbResultRange, &$value])) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
115    }
90    public function retrieveRange(string|Builder $sql, array $params = [], ?DBResultRange $dbResultRange = null, bool $callHooks = true): Iterable
91    {
92        if ($callHooks === true) {
 
93            $trace = debug_backtrace();
94            // Call hooks based on the calling entity, assuming
95            // this method is only called by a subclass. Results
96            // in hook calls named e.g. "DAO_CLASS::_get..."
97            $value = null;
98            if (Hook::run(strtolower($trace[1]['class'] . '::_' . $trace[1]['function']), [&$sql, &$params, &$dbResultRange, &$value])) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
115    }
90    public function retrieveRange(string|Builder $sql, array $params = [], ?DBResultRange $dbResultRange = null, bool $callHooks = true): Iterable
91    {
92        if ($callHooks === true) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
104            $limit = (int) $dbResultRange->getCount();
105            $offset = (int) $dbResultRange->getOffset();
106            $offset += max(0, $dbResultRange->getPage() - 1) * (int) $dbResultRange->getCount();
107            if ($sql instanceof Builder) {
 
107            if ($sql instanceof Builder) {
108                $sql->limit($limit)->offset($offset);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
115    }
90    public function retrieveRange(string|Builder $sql, array $params = [], ?DBResultRange $dbResultRange = null, bool $callHooks = true): Iterable
91    {
92        if ($callHooks === true) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
104            $limit = (int) $dbResultRange->getCount();
105            $offset = (int) $dbResultRange->getOffset();
106            $offset += max(0, $dbResultRange->getPage() - 1) * (int) $dbResultRange->getCount();
107            if ($sql instanceof Builder) {
 
107            if ($sql instanceof Builder) {
108                $sql->limit($limit)->offset($offset);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
115    }
90    public function retrieveRange(string|Builder $sql, array $params = [], ?DBResultRange $dbResultRange = null, bool $callHooks = true): Iterable
91    {
92        if ($callHooks === true) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
104            $limit = (int) $dbResultRange->getCount();
105            $offset = (int) $dbResultRange->getOffset();
106            $offset += max(0, $dbResultRange->getPage() - 1) * (int) $dbResultRange->getCount();
107            if ($sql instanceof Builder) {
 
110                $sql .= " LIMIT {$limit} OFFSET {$offset}";
111            }
112        }
113
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
115    }
90    public function retrieveRange(string|Builder $sql, array $params = [], ?DBResultRange $dbResultRange = null, bool $callHooks = true): Iterable
91    {
92        if ($callHooks === true) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
104            $limit = (int) $dbResultRange->getCount();
105            $offset = (int) $dbResultRange->getOffset();
106            $offset += max(0, $dbResultRange->getPage() - 1) * (int) $dbResultRange->getCount();
107            if ($sql instanceof Builder) {
 
110                $sql .= " LIMIT {$limit} OFFSET {$offset}";
111            }
112        }
113
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
115    }
90    public function retrieveRange(string|Builder $sql, array $params = [], ?DBResultRange $dbResultRange = null, bool $callHooks = true): Iterable
91    {
92        if ($callHooks === true) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
115    }
90    public function retrieveRange(string|Builder $sql, array $params = [], ?DBResultRange $dbResultRange = null, bool $callHooks = true): Iterable
91    {
92        if ($callHooks === true) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
115    }
90    public function retrieveRange(string|Builder $sql, array $params = [], ?DBResultRange $dbResultRange = null, bool $callHooks = true): Iterable
91    {
92        if ($callHooks === true) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
104            $limit = (int) $dbResultRange->getCount();
105            $offset = (int) $dbResultRange->getOffset();
106            $offset += max(0, $dbResultRange->getPage() - 1) * (int) $dbResultRange->getCount();
107            if ($sql instanceof Builder) {
 
107            if ($sql instanceof Builder) {
108                $sql->limit($limit)->offset($offset);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
115    }
90    public function retrieveRange(string|Builder $sql, array $params = [], ?DBResultRange $dbResultRange = null, bool $callHooks = true): Iterable
91    {
92        if ($callHooks === true) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
104            $limit = (int) $dbResultRange->getCount();
105            $offset = (int) $dbResultRange->getOffset();
106            $offset += max(0, $dbResultRange->getPage() - 1) * (int) $dbResultRange->getCount();
107            if ($sql instanceof Builder) {
 
107            if ($sql instanceof Builder) {
108                $sql->limit($limit)->offset($offset);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
115    }
90    public function retrieveRange(string|Builder $sql, array $params = [], ?DBResultRange $dbResultRange = null, bool $callHooks = true): Iterable
91    {
92        if ($callHooks === true) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
104            $limit = (int) $dbResultRange->getCount();
105            $offset = (int) $dbResultRange->getOffset();
106            $offset += max(0, $dbResultRange->getPage() - 1) * (int) $dbResultRange->getCount();
107            if ($sql instanceof Builder) {
 
110                $sql .= " LIMIT {$limit} OFFSET {$offset}";
111            }
112        }
113
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
115    }
90    public function retrieveRange(string|Builder $sql, array $params = [], ?DBResultRange $dbResultRange = null, bool $callHooks = true): Iterable
91    {
92        if ($callHooks === true) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
104            $limit = (int) $dbResultRange->getCount();
105            $offset = (int) $dbResultRange->getOffset();
106            $offset += max(0, $dbResultRange->getPage() - 1) * (int) $dbResultRange->getCount();
107            if ($sql instanceof Builder) {
 
110                $sql .= " LIMIT {$limit} OFFSET {$offset}";
111            }
112        }
113
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
115    }
90    public function retrieveRange(string|Builder $sql, array $params = [], ?DBResultRange $dbResultRange = null, bool $callHooks = true): Iterable
91    {
92        if ($callHooks === true) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
115    }
90    public function retrieveRange(string|Builder $sql, array $params = [], ?DBResultRange $dbResultRange = null, bool $callHooks = true): Iterable
91    {
92        if ($callHooks === true) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
103        if ($dbResultRange && $dbResultRange->isValid()) {
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
 
114        return $sql instanceof Builder ? $sql->get() : DB::cursor(DB::raw($sql)->getValue(DB::connection()->getQueryGrammar()), $params);
115    }
DAO->update
159    public function update(string $sql, array $params = [], bool $callHooks = true, bool $dieOnError = true): int
160    {
161        if ($callHooks === true) {
 
162            $trace = debug_backtrace();
163            // Call hooks based on the calling entity, assuming
164            // this method is only called by a subclass. Results
165            // in hook calls named e.g. "DAO_CLASS::_updateobject"
166            // (all lowercase)
167            $value = null;
168            if (Hook::run(strtolower($trace[1]['class'] . '::_' . $trace[1]['function']), [&$sql, &$params, &$value])) {
 
169                return $value;
159    public function update(string $sql, array $params = [], bool $callHooks = true, bool $dieOnError = true): int
160    {
161        if ($callHooks === true) {
 
162            $trace = debug_backtrace();
163            // Call hooks based on the calling entity, assuming
164            // this method is only called by a subclass. Results
165            // in hook calls named e.g. "DAO_CLASS::_updateobject"
166            // (all lowercase)
167            $value = null;
168            if (Hook::run(strtolower($trace[1]['class'] . '::_' . $trace[1]['function']), [&$sql, &$params, &$value])) {
 
173        return DB::affectingStatement($sql, $params);
174    }
159    public function update(string $sql, array $params = [], bool $callHooks = true, bool $dieOnError = true): int
160    {
161        if ($callHooks === true) {
 
173        return DB::affectingStatement($sql, $params);
174    }
DAO->updateDataObjectSettings
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
 
474                                assert(false);
475                                continue;
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
 
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
 
474                                assert(false);
475                                continue;
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
 
474                                assert(false);
475                                continue;
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
 
474                                assert(false);
475                                continue;
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
 
474                                assert(false);
475                                continue;
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
 
468                        if ($isTranslated) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
 
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
 
468                        if ($isTranslated) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
 
468                        if ($isTranslated) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
 
468                        if ($isTranslated) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
 
468                        if ($isTranslated) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
 
468                        if ($isTranslated) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
 
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
 
468                        if ($isTranslated) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
 
468                        if ($isTranslated) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
 
468                        if ($isTranslated) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
 
468                        if ($isTranslated) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
 
468                        if ($isTranslated) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
 
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
 
468                        if ($isTranslated) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
 
468                        if ($isTranslated) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
 
468                        if ($isTranslated) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
 
468                        if ($isTranslated) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
 
468                        if ($isTranslated) {
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
 
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
 
468                        if ($isTranslated) {
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
 
468                        if ($isTranslated) {
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
 
468                        if ($isTranslated) {
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
 
468                        if ($isTranslated) {
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
 
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
 
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
 
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
 
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
468                        if ($isTranslated) {
 
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
486                        foreach ($values as $locale => $value) {
 
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
 
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
467                    if ($dataObject->hasData($fieldName)) {
 
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
 
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
 
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
 
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
 
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
 
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
 
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
 
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
506            $removeWhere = '';
507            $removeParams = [];
508            foreach ($idArray as $idField => $idValue) {
 
508            foreach ($idArray as $idField => $idValue) {
509                if (!empty($removeWhere)) {
510                    $removeWhere .= ' AND ';
511                }
512                $removeWhere .= $idField . ' = ?';
513                $removeParams[] = $idValue;
514            }
515            $removeWhere .= rtrim(' AND setting_name IN ( ' . str_repeat('? ,', count($staleSettings)), ',') . ')';
516            $removeParams = array_merge($removeParams, $staleSettings);
517            $removeSql = 'DELETE FROM ' . $tableName . ' WHERE ' . $removeWhere;
518            $this->update($removeSql, $removeParams);
519        }
520    }
 
520    }
432    public function updateDataObjectSettings(string $tableName, DataObject $dataObject, array $idArray)
433    {
434        // Initialize variables
435        $idFields = array_keys($idArray);
436        $idFields[] = 'locale';
437        $idFields[] = 'setting_name';
438
439        // Build a data structure that we can process efficiently.
440        $translated = $metadata = 1;
441        $settings = !$metadata;
442        $settingFields = [
443            // Translated data
444            $translated => [
445                $settings => $this->getLocaleFieldNames(),
446                $metadata => $dataObject->getLocaleMetadataFieldNames()
447            ],
448            // Shared data
449            !$translated => [
450                $settings => $this->getAdditionalFieldNames(),
451                $metadata => $dataObject->getAdditionalMetadataFieldNames()
452            ]
453        ];
454
455        // Loop over all fields and update them in the settings table
456        $updateArray = $idArray;
457        $noLocale = 0;
458        $staleSettings = [];
459
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
 
460        foreach ($settingFields as $isTranslated => $fieldTypes) {
461            foreach ($fieldTypes as $isMetadata => $fieldNames) {
462                foreach ($fieldNames as $fieldName) {
463                    // Now we have the following control data:
464                    // - $isTranslated: true for translated data, false data shared between locales
465                    // - $isMetadata: true for metadata fields, false for normal settings
466                    // - $fieldName: the field in the data object to be updated
467                    if ($dataObject->hasData($fieldName)) {
468                        if ($isTranslated) {
469                            // Translated data comes in as an array
470                            // with the locale as the key.
471                            $values = $dataObject->getData($fieldName) ?? [];
472                            if (!is_array($values)) {
473                                // Inconsistent data: should have been an array
474                                assert(false);
475                                continue;
476                            }
477                        } else {
478                            // Transform shared data into an array so that
479                            // we can handle them the same way as translated data.
480                            $values = [
481                                $noLocale => $dataObject->getData($fieldName)
482                            ];
483                        }
484
485                        // Loop over the values and update them in the database
486                        foreach ($values as $locale => $value) {
487                            $updateArray['locale'] = ($locale === $noLocale ? '' : $locale);
488                            $updateArray['setting_name'] = $fieldName;
489                            $updateArray['setting_type'] = null;
490                            // Convert the data value and implicitly set the setting type.
491                            $updateArray['setting_value'] = $this->convertToDB($value, $updateArray['setting_type']);
492                            $this->replace($tableName, $updateArray, $idFields);
493                        }
494                    } else {
495                        // Data is maintained "sparsely". Only set fields will be
496                        // recorded in the settings table. Fields that are not explicity set
497                        // in the data object will be deleted.
498                        $staleSettings[] = $fieldName;
499                    }
500                }
501            }
502        }
503
504        // Remove stale data
505        if (count($staleSettings)) {
 
520    }